using System;
using System.IO;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;

using CILFlow;

namespace CILFlow.Pretty
{
    class CodeRepresentation
    {
        public static string Format( PopTree p )
        {
            // Pop 1, Push 1
            if (p._instruction.OpCode.StackBehaviourPop == StackBehaviour.Pop1 &&
                p._instruction.OpCode.StackBehaviourPush == StackBehaviour.Push1)
            {
                if (p._instruction.OpCode == OpCodes.Ldfld ||
                    p._instruction.OpCode == OpCodes.Ldflda)
                {
                    return Retrieval.Delegate(p);
                }
                return Transforms.Delegate(p);
            }

            // Pop 0, Push 1
            if (p._instruction.OpCode.StackBehaviourPop == StackBehaviour.Pop0 &&
                p._instruction.OpCode.StackBehaviourPush == StackBehaviour.Push1)
            {
                return Retrieval.Delegate(p);
            }
            return "";
        }
    }

    class Transforms
    {
        public static string Delegate(PopTree p)
        {
            if (p._instruction.OpCode == OpCodes.Castclass)
                return Transforms.Cast(p);
            if (p._instruction.OpCode == OpCodes.Isinst)
                return Transforms.Is(p);
            if (p._instruction.OpCode == OpCodes.Neg)
                return Transforms.Is(p);
            if (p._instruction.OpCode == OpCodes.Not)
                return Transforms.Is(p);
            // Silent: Box, Implicit Conversion, Unbox.
            return CodeRepresentation.Format( p._PopList[0] );
        }

        public static string Cast( PopTree p )
        {
            TypeReference tr = (TypeReference)p._instruction.Operand;
            return "(" + tr.Name + ")" + CodeRepresentation.Format(p._PopList[0]);
        }
        public static string Is(PopTree p)
        {
            TypeReference tr = (TypeReference)p._instruction.Operand;
            return CodeRepresentation.Format(p._PopList[0]) + "is typeof(" + tr.Name + ")";
        }
        public static string Neg(PopTree p)
        {
            return "-" + CodeRepresentation.Format(p._PopList[0]);
        }
        public static string Not(PopTree p)
        {
            return "!" + CodeRepresentation.Format(p._PopList[0]);
        }

        public static string Length(PopTree p)
        {
            return CodeRepresentation.Format(p._PopList[0]) + ".Length";    
        }
    }

    class Retrieval
    {
        public static string Delegate(PopTree p)
        {
            if (p._instruction.OpCode == OpCodes.Ldfld)
                return Retrieval.Field(p);
            if (p._instruction.OpCode == OpCodes.Ldflda)
                return Retrieval.FieldAddress(p);

            if (p._instruction.OpCode.OperandType == OperandType.InlineParam )
                return Retrieval.LdArg(p);
            if( p._instruction.OpCode.OperandType == OperandType.InlineVar )
                return Retrieval.LdVar(p);
            if (p._instruction.OpCode.OperandType == OperandType.InlineField)
                return Retrieval.StaticField(p);

            if (p._instruction.OpCode.OperandType == OperandType.InlineI ||
                p._instruction.OpCode.OperandType == OperandType.InlineI8 ||
                p._instruction.OpCode.OperandType == OperandType.InlineR ||
                p._instruction.OpCode.OperandType == OperandType.ShortInlineI)
                return Retrieval.LdConstant(p);

            if (p._instruction.OpCode == OpCodes.Ldnull)
                return Retrieval.LdNull(p);

            if (p._instruction.OpCode == OpCodes.Sizeof)
                return Retrieval.Sizeof(p);

            if (p._instruction.OpCode == OpCodes.Ldstr)
                return Retrieval.LdStr(p);

            if (p._PopList.Count > 0)
                return CodeRepresentation.Format(p._PopList[0]);

            return "";
        }

        public static string Field(PopTree p)
        {
            PopTree obj = p._PopList[0];
            FieldReference fr = (FieldReference)p._instruction.Operand;
            return CodeRepresentation.Format(obj) + "." + fr.Name;
        }
        public static string FieldAddress(PopTree p)
        {
            return "&" + Field(p);
        }

        public static string StaticField(PopTree p)
        {
            FieldReference fr = (FieldReference)p._instruction.Operand;
            return fr.DeclaringType.Name + "." + fr.Name;
        }

        public static string LdArg(PopTree p)
        {
            ParameterReference pr = (ParameterReference)CILUtil.Entity.GetEntity(p._md, p._instruction);
            return pr.Name;
        }
        public static string LdVar(PopTree p)
        {
            VariableReference vr = (VariableReference) CILUtil.Entity.GetEntity(p._md, p._instruction);
            return vr.Name;
        }

        public static string LdStr(PopTree p)
        {
            string str = p._instruction.Operand as string;
            return "\"" + CecilFormatter.ToInvariantCultureString(str) + "\"";
        }

        public static string LdConstant(PopTree p)
        {
            if (p._instruction.OpCode == OpCodes.Ldc_I4 ||
                p._instruction.OpCode == OpCodes.Ldc_I4_S ||
                p._instruction.OpCode == OpCodes.Ldc_I8 ||
                p._instruction.OpCode == OpCodes.Ldc_R4 ||
                p._instruction.OpCode == OpCodes.Ldc_R8 )
            {
                return p._instruction.Operand.ToString();
            }

            if (p._instruction.OpCode == OpCodes.Ldc_I4_M1) return "-1";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_0) return "0";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_1) return "1";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_2) return "2";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_3) return "3";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_4) return "4";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_5) return "5";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_6) return "6";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_7) return "7";
            if (p._instruction.OpCode == OpCodes.Ldc_I4_8) return "8";

            return "";
        }
        public static string LdNull(PopTree p)
        {
            return "null";
        }
        public static string Sizeof(PopTree p)
        {
            TypeReference sizeoftype = (TypeReference)p._instruction.Operand;
            //int sizeofsize = state.runtime.Resolver.ResolveType(sizeoftype).GetSizeOf();
            return "sizeof( " + sizeoftype.Name + ")";
        }


        public static string LoadArray(PopTree p)
        {
            string var = CodeRepresentation.Format(p._PopList[1]);
            string ii = CodeRepresentation.Format(p._PopList[0]);
            return var + "[" + ii + "]";
        }
    }

    class Storage
    {
        public static string Delegate(PopTree p)
        {
            if (p._instruction.OpCode == OpCodes.Stfld)
                return Storage.StoreField(p);

            if (p._instruction.OpCode == OpCodes.Stsfld)
                return Storage.StoreStaticField(p);
            if (p._instruction.OpCode == OpCodes.Stloc ||
                p._instruction.OpCode == OpCodes.Stloc_0 ||
                p._instruction.OpCode == OpCodes.Stloc_1 ||
                p._instruction.OpCode == OpCodes.Stloc_2 ||
                p._instruction.OpCode == OpCodes.Stloc_3 ||
                p._instruction.OpCode == OpCodes.Stloc_S )
                return Storage.StoreVar(p);
            if (p._instruction.OpCode == OpCodes.Starg ||
                p._instruction.OpCode == OpCodes.Starg_S)
                return Storage.StoreArg(p);

            return "";
        }

        public static string StoreField(PopTree p)
        {
            PopTree obj = p._PopList[0];
            FieldReference fr = (FieldReference)p._instruction.Operand;
            return CodeRepresentation.Format(obj) + "." + fr.Name + "=";
        }

        public static string StoreArray(PopTree p)
        {
            string var = CodeRepresentation.Format(p._PopList[1]);
            string ii  = CodeRepresentation.Format(p._PopList[0]);
            return var + "[" + ii + "]" + "=";
        }


        public static string StoreStaticField(PopTree p)
        {
            FieldReference fr = (FieldReference)p._instruction.Operand;
            return fr.DeclaringType.Name + "." + fr.Name + "=";
        }

        public static string StoreArg(PopTree p)
        {
            ParameterReference pr = (ParameterReference)CILUtil.Entity.GetEntity(p._md, p._instruction);
            return pr.Name + "=";
        }
        public static string StoreVar(PopTree p)
        {
            VariableReference vr = (VariableReference)CILUtil.Entity.GetEntity(p._md, p._instruction);
            return vr.Name + "=";
        }
    }

    class Control
    {

    }

    class Special
    {
    }

    class More
    {

    }
}
